package com.woody.aggregate.application.manage.messagebox;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.woody.aggregate.application.service.messagebox.IMessageboxApplicationConfigService;
import com.woody.aggregate.application.service.messagebox.IMessageboxConfigService;
import com.woody.aggregate.domain.msgsendrule.MsgSendRuleManager;
import com.woody.aggregate.domain.msgsendrule.MsgSendRuleTargetRefManager;
import com.woody.aggregate.infrastructure.config.RedisKeyConst;
import com.woody.aggregate.infrastructure.messagebox.entity.MessageboxApplicationConfig;
import com.woody.aggregate.infrastructure.messagebox.entity.MessageboxConfig;
import com.woody.aggregate.infrastructure.messagebox.wrapmodel.MessageboxListQuery;
import com.woody.aggregate.infrastructure.msgsendrule.entity.MsgSendRule;
import com.woody.aggregate.infrastructure.msgsendrule.entity.MsgSendRuleTargetRef;
import com.woody.aggregate.messagebox.dto.MessageboxAddDTO;
import com.woody.aggregate.messagebox.dto.MessageboxEditDTO;
import com.woody.aggregate.messagebox.dto.MessageboxListDTO;
import com.woody.aggregate.messagebox.vo.MessageboxApplicationVO;
import com.woody.aggregate.messagebox.vo.MessageboxDetailVO;
import com.woody.aggregate.messagebox.vo.MessageboxInfoVO;
import com.woody.aggregate.messagebox.vo.MessageboxListVO;
import com.woody.aggregate.msgsendrule.enums.ClientEnum;
import com.woody.aggregate.msgsendrule.enums.MessageboxStatusEnum;
import com.woody.aggregate.msgsendrule.enums.MsgSendUserScopeEnum;
import com.woody.base.exception.BaseException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

@Component
@Slf4j
public class MessageboxManage {

    @Autowired
    private IMessageboxConfigService messageboxConfigService;

    @Autowired
    private IMessageboxApplicationConfigService messageboxApplicationConfigService;

    @Autowired
    private MsgSendRuleManager msgSendRuleManager;

    @Autowired
    private MsgSendRuleTargetRefManager msgSendRuleTargetRefManager;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    public Page<MessageboxListVO> list(MessageboxListDTO dto) {
        List<MessageboxListVO> result = new ArrayList<>();

        MessageboxListQuery query = new MessageboxListQuery();
        if(dto != null) {
            BeanUtils.copyProperties(dto, query);
        }
        /**根据查询条件筛选弹框**/
        Page<MessageboxConfig> pages = messageboxConfigService.getListByDto(dto.genPage(),query);
        if(pages == null || CollectionUtils.isEmpty(pages.getRecords())) {
            return new Page<>(dto.getSize(),0);
        }
        /**查询列表其他数据**/
        List<MessageboxConfig> messageboxConfigs = pages.getRecords();
        List<Long> ids = messageboxConfigs.stream().map(MessageboxConfig::getId).collect(Collectors.toList());
        List<MessageboxApplicationConfig> messageboxApplicationConfigs =
                messageboxApplicationConfigService.list(new QueryWrapper<MessageboxApplicationConfig>().lambda().in(MessageboxApplicationConfig::getConfigId, ids));
        Map<Long, List<MessageboxApplicationConfig>> messageboxApplicationConfigMap = messageboxApplicationConfigs.stream().collect(Collectors.groupingBy(MessageboxApplicationConfig::getConfigId));

        Map<Integer, MsgSendRule> msgSendRuleMap = new HashMap<>();
        List<Integer> ruleIds = messageboxConfigs.stream().filter(item->item.getPushRuleId() != null).map(MessageboxConfig::getPushRuleId).collect(Collectors.toList());
        if(CollectionUtils.isNotEmpty(ruleIds)) {
            List<MsgSendRule> msgSendRules = msgSendRuleManager.listByIds(ruleIds);
            msgSendRuleMap = msgSendRules.stream().collect(Collectors.toMap(MsgSendRule::getId, Function.identity(), (v1, v2) -> v1));
        }
        /**组合弹框列表其他数据**/
        for(MessageboxConfig config : messageboxConfigs) {
            MessageboxListVO vo = new MessageboxListVO();
            BeanUtils.copyProperties(config, vo);
            if(messageboxApplicationConfigMap.containsKey(config.getId())) {
                List<MessageboxApplicationConfig> configs = messageboxApplicationConfigMap.get(config.getId());
                StringBuilder popupNames = new StringBuilder();
                StringBuilder priorities = new StringBuilder();
                for(MessageboxApplicationConfig applicationConfig : configs) {
                    popupNames.append(applicationConfig.getPopupName()).append(",");
                    priorities.append(applicationConfig.getPriority()).append(",");
                }
                if(StringUtils.isNotBlank(popupNames)) {
                    popupNames.deleteCharAt(popupNames.length() - 1);
                }
                if(StringUtils.isNotBlank(priorities)) {
                    priorities.deleteCharAt(priorities.length() - 1);
                }
                vo.setPopupNames(popupNames.toString());
                vo.setPriorities(priorities.toString());
            }
            if(msgSendRuleMap.containsKey(config.getPushRuleId())) {
                MsgSendRule rule = msgSendRuleMap.get(config.getPushRuleId());
                vo.setRuleName(rule.getTitle());
                vo.setRuleId(rule.getId());
            }
            result.add(vo);
        }
        Page<MessageboxListVO> resultPage = new Page<MessageboxListVO>(dto.getSize(), pages.getPages(), pages.getTotal());
        resultPage.setRecords(result);
        return resultPage;
    }

    @Transactional
    public Boolean delete(Long id) {
        MessageboxConfig messageboxConfig = messageboxConfigService.getById(id);
        if(messageboxConfig == null) {
            throw new BaseException("弹框不存在");
        }
        if(MessageboxStatusEnum.ENABLE.getStatus().equals(messageboxConfig.getStatus())) {
            throw new BaseException("启用弹框禁止删除,请先禁用弹框");
        }
        messageboxConfigService.removeById(id);
        messageboxApplicationConfigService.remove(new QueryWrapper<MessageboxApplicationConfig>().lambda().eq(MessageboxApplicationConfig::getConfigId, id));
        return true;
    }

    public MessageboxDetailVO detail(Long id) {

        MessageboxDetailVO result = new MessageboxDetailVO();

        MessageboxConfig messageboxConfig = messageboxConfigService.getById(id);
        if(messageboxConfig == null) {
            return null;
        }
        /**查询弹框其他数据**/
        List<MessageboxApplicationConfig> messageboxApplicationConfigs =
                messageboxApplicationConfigService.list(new QueryWrapper<MessageboxApplicationConfig>().lambda().eq(MessageboxApplicationConfig::getConfigId, id));
        Map<Long, List<MessageboxApplicationConfig>> messageboxApplicationConfigMap = messageboxApplicationConfigs.stream().collect(Collectors.groupingBy(MessageboxApplicationConfig::getConfigId));

        MsgSendRule msgSendRule = null;
        if(messageboxConfig.getPushRuleId() != null) {
            msgSendRule = msgSendRuleManager.getById(messageboxConfig.getPushRuleId());
        }

        BeanUtils.copyProperties(messageboxConfig, result);
        if(msgSendRule != null) {
            result.setRuleName(msgSendRule.getTitle());
            result.setRuleId(msgSendRule.getId());
        }
        /**组合弹框其他数据**/
        if(messageboxApplicationConfigMap.containsKey(id)) {
            List<MessageboxApplicationVO> applications = new ArrayList<>();
            List<MessageboxApplicationConfig> configs = messageboxApplicationConfigMap.get(id);
            for (MessageboxApplicationConfig config : configs) {
                MessageboxApplicationVO vo = new MessageboxApplicationVO();
                BeanUtils.copyProperties(config, vo);
                applications.add(vo);
            }
            result.setApplications(applications);
        }
        return result;
    }

    @Transactional
    public Boolean edit(MessageboxEditDTO dto) {

        MessageboxConfig messageboxConfig = messageboxConfigService.getById(dto.getId());
        if(messageboxConfig == null) {
            throw new BaseException("弹框不存在");
        }
        if(!MessageboxStatusEnum.UN_ENABLE.getStatus().equals(messageboxConfig.getStatus())) {
            throw new BaseException("弹框不是未启用状态,禁止编辑弹框");
        }
        //校验规则存在
        if(dto.getPushRuleId() != null) {
            MsgSendRule msgSendRule = msgSendRuleManager.getById(dto.getPushRuleId());
            if(msgSendRule == null) {
                throw new BaseException("推送规则不存在");
            }
        }

        //校验title是否重复
        List<MessageboxConfig> messageboxConfigs = messageboxConfigService.list(new QueryWrapper<MessageboxConfig>().lambda().eq(MessageboxConfig::getTitle, dto.getTitle()));
        if(CollectionUtils.isNotEmpty(messageboxConfigs)) {
            for (MessageboxConfig config : messageboxConfigs) {
                if(config.getId().equals(dto.getId())) {
                    continue;
                }
                throw new BaseException("弹框标题重复");
            }
        }
        messageboxConfigService.updateByDto(messageboxConfig,dto);
        messageboxApplicationConfigService.updateConfig(messageboxConfig,dto);
        return true;
    }

    @Transactional
    public Boolean add(MessageboxAddDTO dto) {

        //校验title是否重复
        List<MessageboxConfig> messageboxConfigs = messageboxConfigService.list(new QueryWrapper<MessageboxConfig>().lambda().eq(MessageboxConfig::getTitle, dto.getTitle()));
        if(CollectionUtils.isNotEmpty(messageboxConfigs)) {
            throw new BaseException("弹框标题重复");
        }
        //校验规则存在
        if(dto.getPushRuleId() != null) {
            MsgSendRule msgSendRule = msgSendRuleManager.getById(dto.getPushRuleId());
            if(msgSendRule == null) {
                throw new BaseException("推送规则不存在");
            }
        }
        MessageboxConfig messageboxConfig = messageboxConfigService.add(dto);
        messageboxApplicationConfigService.add(dto,messageboxConfig);

        return true;
    }

    @Transactional
    public boolean enable(Long id, Boolean enabled) {
        MessageboxConfig messageboxConfig = messageboxConfigService.getById(id);
        if(messageboxConfig == null) {
            throw new BaseException("弹框不存在");
        }
        if(enabled &&MessageboxStatusEnum.ENABLE.getStatus().equals(messageboxConfig.getStatus())) {
            throw new BaseException("弹框已经是已启用状态,禁止操作");
        }
        if(!enabled && !MessageboxStatusEnum.ENABLE.getStatus().equals(messageboxConfig.getStatus())) {
            throw new BaseException("弹框不是启用状态,禁止操作");
        }
        int status = enabled ? MessageboxStatusEnum.ENABLE.getStatus() : MessageboxStatusEnum.DISABLE.getStatus();

        if(messageboxConfig.getPushRuleId() == null && enabled) {
            throw new BaseException("弹框未关联推送规则,请先关联推送规则");
        }
        messageboxConfigService.update(new UpdateWrapper<MessageboxConfig>().lambda().eq(MessageboxConfig::getId, id).set(MessageboxConfig::getStatus, status));
        //更新缓存
        if(messageboxConfig.getPushRuleId() != null) {
            String applications = messageboxConfig.getApplication();
            if(StringUtils.isNotBlank(applications)) {
                List<String> applicationsList = Arrays.asList(applications.split(","));
                MsgSendRule rule = msgSendRuleManager.getById(messageboxConfig.getPushRuleId());
                if(rule == null) {
                    throw new BaseException("推送规则不存在");
                }
                if(rule.getUserScope() == MsgSendUserScopeEnum.ALL.getScope()) {
                    for (String client : applicationsList) {
                        redisTemplate.delete(RedisKeyConst.MESSAGEBOX_CONFIG_SCOPE_KEY+client);
                    }
                }else {
                    List<MsgSendRuleTargetRef> msgSendRuleTargetRefs = msgSendRuleTargetRefManager.list(new QueryWrapper<MsgSendRuleTargetRef>().lambda().eq(MsgSendRuleTargetRef::getRuleId, rule.getId()));
                    for (MsgSendRuleTargetRef msgSendRuleTargetRef : msgSendRuleTargetRefs) {
                        for (String client : applicationsList) {
                            redisTemplate.delete(RedisKeyConst.MESSAGEBOX_CONFIG_KEY+client+":"+msgSendRuleTargetRef.getBizId());
                        }
                    }
                }
            }

        }

        return true;
    }

    public List<MessageboxInfoVO> messageboxInfo(Long bizId, String client) {
        if(ClientEnum.getByType(client) == null) {
            throw new BaseException("客户端类型错误");
        }
        List<MessageboxInfoVO> messagebox = new ArrayList<>();
        List<MessageboxInfoVO> scopeMessagebox = new ArrayList<>();
        //查询缓存
        String scopeValue = (String) redisTemplate.opsForValue().get(RedisKeyConst.MESSAGEBOX_CONFIG_SCOPE_KEY+client);
        String value = (String) redisTemplate.opsForValue().get(RedisKeyConst.MESSAGEBOX_CONFIG_KEY+client+":"+bizId);
        if(scopeValue == null) {//无缓存,从db获取
            scopeMessagebox = loadScopeMessagebox(client);
        }else {
            scopeMessagebox = JSONArray.parseArray(scopeValue, MessageboxInfoVO.class);
        }

        if(value == null) {//无缓存,从db获取
            messagebox = loadMessagebox(bizId, client);
            redisTemplate.opsForValue().set(RedisKeyConst.MESSAGEBOX_CONFIG_KEY+client+":"+bizId, JSONArray.toJSONString(messagebox), 24, TimeUnit.HOURS);
        }else {
            messagebox = JSONArray.parseArray(value, MessageboxInfoVO.class);
        }
        messagebox.addAll(scopeMessagebox);
        Long currentTime = System.currentTimeMillis();
        messagebox = messagebox.stream().filter(p -> p.getStartTime() != null && p.getStartTime().getTime() < currentTime).collect(Collectors.toList());
        if(!CollectionUtils.isEmpty(messagebox)) {
            messagebox = messagebox.stream().sorted(Comparator.comparing(MessageboxInfoVO::getPriority)).collect(Collectors.toList());
            messagebox.stream().forEach(p -> p.setBizId(String.valueOf(bizId)));
        }
        return messagebox;
    }

    private List<MessageboxInfoVO> loadMessagebox(Long bizId, String client) {
        List<MessageboxInfoVO> messagebox = new ArrayList<>();
        //查询规则信息
        List<MsgSendRuleTargetRef> msgSendRuleTargetRefs = msgSendRuleTargetRefManager.list(new QueryWrapper<MsgSendRuleTargetRef>().lambda().eq(MsgSendRuleTargetRef::getBizId, bizId));
        if(CollectionUtils.isEmpty(msgSendRuleTargetRefs)) {
            redisTemplate.opsForValue().set(RedisKeyConst.MESSAGEBOX_CONFIG_KEY+client+":"+bizId, JSONArray.toJSONString(new ArrayList<>()), 24, TimeUnit.HOURS);
            return messagebox;
        }
        List<Integer> ruleIds = msgSendRuleTargetRefs.stream().map(MsgSendRuleTargetRef::getRuleId).collect(Collectors.toList());
        List<MsgSendRule> targetMsgSendRules = msgSendRuleManager.list(new QueryWrapper<MsgSendRule>().lambda().in(MsgSendRule::getId, ruleIds));
        if(CollectionUtils.isEmpty(targetMsgSendRules)) {
            redisTemplate.opsForValue().set(RedisKeyConst.MESSAGEBOX_CONFIG_KEY+client+":"+bizId, JSONArray.toJSONString(new ArrayList<>()), 24, TimeUnit.HOURS);
            return messagebox;
        }
        List<MessageboxConfig> messageboxConfigs = messageboxConfigService.list(new QueryWrapper<MessageboxConfig>().lambda().in(MessageboxConfig::getPushRuleId, ruleIds).eq(MessageboxConfig::getStatus, 1));
        if(CollectionUtils.isEmpty(messageboxConfigs)) {
            redisTemplate.opsForValue().set(RedisKeyConst.MESSAGEBOX_CONFIG_KEY+client+":"+bizId, JSONArray.toJSONString(new ArrayList<>()), 24, TimeUnit.HOURS);
            return messagebox;
        }

        Map<Integer, MsgSendRule> msgSendRuleMap = targetMsgSendRules.stream().collect(Collectors.toMap(MsgSendRule::getId, item -> item, (item1, item2) -> item1));
        List<Long> configIds = messageboxConfigs.stream().map(p -> p.getId()).collect(Collectors.toList());
        List<MessageboxApplicationConfig> messageboxApplicationConfigs = messageboxApplicationConfigService.list(new QueryWrapper<MessageboxApplicationConfig>().lambda()
                .in(MessageboxApplicationConfig::getConfigId, configIds).eq(MessageboxApplicationConfig::getType, client));
        if(CollectionUtils.isEmpty(messageboxApplicationConfigs)) {
            redisTemplate.opsForValue().set(RedisKeyConst.MESSAGEBOX_CONFIG_KEY+client+":"+bizId, JSONArray.toJSONString(new ArrayList<>()), 24, TimeUnit.HOURS);
            return messagebox;
        }

        Map<Long, MessageboxApplicationConfig> messageboxApplicationConfigMap = messageboxApplicationConfigs.stream().collect(Collectors.toMap(MessageboxApplicationConfig::getConfigId, item -> item, (item1, item2) -> item1));
        for (MessageboxConfig messageboxConfig : messageboxConfigs) {
            MessageboxInfoVO vo = new MessageboxInfoVO();
            MessageboxApplicationConfig messageboxApplicationConfig = messageboxApplicationConfigMap.get(messageboxConfig.getId());
            MsgSendRule msgSendRule = msgSendRuleMap.get(messageboxConfig.getPushRuleId());
            if(msgSendRule == null) continue;
            vo.setStartTime(msgSendRule.getStartTime());
            vo.setFrequency(msgSendRule.getFrequency());
            if(messageboxApplicationConfig == null) continue;
            vo.setId(messageboxConfig.getId());
            vo.setJumpLink(messageboxApplicationConfig.getJumpLink());
            if(StringUtils.isNotEmpty(messageboxConfig.getMessageboxTemplate())) {
                try{
                    Map<String, Object> template = JSONObject.parseObject(messageboxConfig.getMessageboxTemplate(),Map.class);
                    vo.setMessageboxTemplate((String) template.get("type"));
                }catch (Exception e) {
                    log.error("messageboxConfig.getMessageboxTemplate() error", e);
                }
            }
            vo.setPictureUrl(messageboxConfig.getPictureUrl());
            vo.setPriority(messageboxApplicationConfig.getPriority());
            vo.setPopupUrl(messageboxApplicationConfig.getPopupUrl());
            messagebox.add(vo);
        }
        redisTemplate.opsForValue().set(RedisKeyConst.MESSAGEBOX_CONFIG_KEY+client+":"+bizId, JSONArray.toJSONString(messagebox), 24, TimeUnit.HOURS);
        return messagebox;
    }

    private List<MessageboxInfoVO> loadScopeMessagebox(String client) {
        List<MessageboxInfoVO> messagebox = new ArrayList<>();

        List<MsgSendRule> scopeMsgSendRules = msgSendRuleManager.list(new QueryWrapper<MsgSendRule>().lambda().eq(MsgSendRule::getUserScope, 1));
        if (CollectionUtils.isEmpty(scopeMsgSendRules)) {
            redisTemplate.opsForValue().set(RedisKeyConst.MESSAGEBOX_CONFIG_SCOPE_KEY + client, JSONArray.toJSONString(new ArrayList<>()), 24, TimeUnit.HOURS);
            return messagebox;
        }
        List<Integer> allRuleIds = scopeMsgSendRules.stream().map(MsgSendRule::getId).collect(Collectors.toList());
        List<MessageboxConfig> messageboxConfigs = messageboxConfigService.list(new QueryWrapper<MessageboxConfig>().lambda().in(MessageboxConfig::getPushRuleId, allRuleIds).eq(MessageboxConfig::getStatus, 1));
        if (CollectionUtils.isEmpty(messageboxConfigs)) {
            redisTemplate.opsForValue().set(RedisKeyConst.MESSAGEBOX_CONFIG_SCOPE_KEY + client, JSONArray.toJSONString(new ArrayList<>()), 24, TimeUnit.HOURS);
            return messagebox;
        }

        Map<Integer, MsgSendRule> msgSendRuleMap = scopeMsgSendRules.stream().collect(Collectors.toMap(MsgSendRule::getId, item -> item, (item1, item2) -> item1));
        List<Long> configIds = messageboxConfigs.stream().map(p -> p.getId()).collect(Collectors.toList());
        List<MessageboxApplicationConfig> messageboxApplicationConfigs = messageboxApplicationConfigService.list(new QueryWrapper<MessageboxApplicationConfig>().lambda()
                .in(MessageboxApplicationConfig::getConfigId, configIds).eq(MessageboxApplicationConfig::getType, client));
        if (CollectionUtils.isEmpty(messageboxApplicationConfigs)) {
            redisTemplate.opsForValue().set(RedisKeyConst.MESSAGEBOX_CONFIG_SCOPE_KEY + client, JSONArray.toJSONString(new ArrayList<>()), 24, TimeUnit.HOURS);
            return messagebox;
        }
        Map<Long, MessageboxApplicationConfig> messageboxApplicationConfigMap = messageboxApplicationConfigs.stream().collect(Collectors.toMap(MessageboxApplicationConfig::getConfigId, item -> item, (item1, item2) -> item1));

        for (MessageboxConfig messageboxConfig : messageboxConfigs) {
            MessageboxInfoVO vo = new MessageboxInfoVO();
            MessageboxApplicationConfig messageboxApplicationConfig = messageboxApplicationConfigMap.get(messageboxConfig.getId());
            MsgSendRule msgSendRule = msgSendRuleMap.get(messageboxConfig.getPushRuleId());
            if (msgSendRule == null) continue;
            vo.setStartTime(msgSendRule.getStartTime());
            vo.setFrequency(msgSendRule.getFrequency());
            if (messageboxApplicationConfig == null) continue;
            vo.setId(messageboxConfig.getId());
            vo.setJumpLink(messageboxApplicationConfig.getJumpLink());
            if(StringUtils.isNotEmpty(messageboxConfig.getMessageboxTemplate())) {
                try{
                    Map<String, Object> template = JSONObject.parseObject(messageboxConfig.getMessageboxTemplate(),Map.class);
                    vo.setMessageboxTemplate((String) template.get("type"));
                }catch (Exception e) {
                    log.error("messageboxConfig.getMessageboxTemplate() error", e);
                }
            }
            vo.setPictureUrl(messageboxConfig.getPictureUrl());
            vo.setPriority(messageboxApplicationConfig.getPriority());
            vo.setPopupUrl(messageboxApplicationConfig.getPopupUrl());
            messagebox.add(vo);
        }
        redisTemplate.opsForValue().set(RedisKeyConst.MESSAGEBOX_CONFIG_SCOPE_KEY + client, JSONArray.toJSONString(messagebox), 24, TimeUnit.HOURS);
        return messagebox;
    }
}
